package org.hibernate.criterion;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.dialect.function.SQLFunctionRegistry;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.type.Type;

/**
 * Rappresentazione di una funzione da utilizzare per creare condizioni complesse 
 * con Criteria 
 * 
 * <p><b>NOTA</b>: questa classe non è presente nel pacchetto standard di Hibernate.</p>
 * @author Nicola Spazzoli
 *
 */
public class SimpleFunction {

	private final String propertyName;
	private final String functionName;
	private final SimpleFunction nestedFunction;

	private SQLFunction function;
	
	protected SimpleFunction(String propertyName, String functionName) {
		this.propertyName = propertyName;
		this.functionName = functionName;
		this.nestedFunction=null;
	}
	
	protected SimpleFunction(SimpleFunction nestedFunction, String functionName) {
		this.propertyName = nestedFunction.getPropertyName();
		this.nestedFunction=nestedFunction;
		this.functionName=functionName;
	}
	
	public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
	throws HibernateException {
		findSQLFunction(criteria, criteriaQuery);
		String[] columns = criteriaQuery.getColumnsUsingProjection(criteria, propertyName);
		StringBuffer fragment = new StringBuffer();
		
		fragment.append(" "+functionName+"(");
		fragment.append( (nestedFunction==null)? columns[0] : nestedFunction.toSqlString(criteria, criteriaQuery) );
		fragment.append(")");
		
		return fragment.toString();
		
	}
	
	private void findSQLFunction(Criteria criteria, CriteriaQuery criteriaQuery){
		if (function==null) {
			SessionFactoryImplementor factory = criteriaQuery.getFactory();
			SQLFunctionRegistry sqlFunctionReg= new SQLFunctionRegistry( factory.getDialect(), java.util.Collections.EMPTY_MAP );
			this.function= sqlFunctionReg.findSQLFunction(functionName);
		}
	}
	
	
	public String toString() {
		return " "+functionName+"("+propertyName+")"; 
	}
	
	public String getFunctionName() {
		return functionName;
	}
	
	public String getPropertyName() {
		return propertyName;
	}


	public Type getType(Criteria criteria, CriteriaQuery criteriaQuery)  {
		findSQLFunction(criteria, criteriaQuery);
		
		return this.function.getReturnType(null, null);
	}
	
}
